package de.fmaul.dicheck.assertions;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.log4j.Logger;

import com.google.common.collect.Lists;

public class AssertionFactory {

    private static Logger log = Logger.getLogger(AssertionFactory.class);

    private final List<AssertionDefinition> definitions = Lists.newArrayList();

    public AssertionFactory() {
        definitions.add(new AssertionDefinition("assert (\\d+) rows") {
            @Override
            public Assertion createAssertion(Matcher matcher, String message) {
                log.debug("found RowCountAssertion: " + matcher.group());
                return new RowCountAssertion(message, Integer.parseInt(matcher
                        .group(1)));
            }
        });
        definitions.add(new AssertionDefinition("assert empty") {
            @Override
            public Assertion createAssertion(Matcher matcher, String message) {
                log.debug("found RowCountAssertion for 0 rows: "
                        + matcher.group());
                return new RowCountAssertion(message, 0);
            }
        });
        definitions.add(new AssertionDefinition(
                "assert column (\\d+) matches (.*)") {
            @Override
            protected Assertion createAssertion(Matcher matcher, String message) {
                log.debug("found RegExpAssertion for pattern "
                        + matcher.group());
                int column = Integer.parseInt(matcher.group(1));
                if (column > 0) {
                    column--;
                    final String pattern = matcher.group(2);
                    return new RegExpAssertion(message, column, pattern);
                }
                return createNullAssertion();
            }
        });

    }

    private Assertion createNullAssertion() {
        return new AbstractAssertion() {};
    }    
    
    private abstract static class AssertionDefinition {
        private final Pattern commandPattern;

        public AssertionDefinition(String newCommand) {
            commandPattern = Pattern.compile(newCommand);
        }

        protected abstract Assertion createAssertion(Matcher cmd, String message);

        public final Assertion parseCommand(String cmd, String message) {
            final Matcher matcher = commandPattern.matcher(cmd);
            if (matcher.matches()) {
                return createAssertion(matcher, message);
            } else {
                return null;
            }
        }
    }

    public Assertion fromSqlString(final String line) {
        Assertion result = null;

        final Pattern p = Pattern.compile("--([^:]+):?((.*))?");
        final Matcher m = p.matcher(line);
        if (m.matches()) {
            final String command = m.group(1).trim();
            String message = "";
            if (m.groupCount() > 2) {
                message = m.group(2).trim();
            }

            for (AssertionDefinition definition : definitions) {
                Assertion assertion = definition.parseCommand(command, message);
                if (assertion != null) {
                    result = assertion;
                    break;
                }
            }
        }

        return result;

    }
}
